home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
-
- use strict;
- use Getopt::Long;
-
- # TODO!!!!!!!!
- # make the MacFileDiff and MacTransferDiff packages use
- # object syntax for variable access.
- # using $Package::variable doesn't warn if you type the variable name incorrectly!
-
- $ENV{'PATH'} = '/Developer/Tools:.:' . $ENV{'PATH'};
-
- my $skipPartialFiles;
- my $singleCompareMode;
-
- GetOptions('c' => \$singleCompareMode,
- 'P' => \$skipPartialFiles);
-
- my $result = 0;
-
- if($singleCompareMode) {
-
- my $usage = "usage: $0 -c [-P] <file1> <file2>\n";
-
- my $file1 = shift @ARGV;
- my $file2 = shift @ARGV;
-
- unless($file1 && $file2) {
- die $usage;
- }
-
- $result = MacFileDiff::compareNodes($file1, $file2, $skipPartialFiles);
- } else {
-
- my $usage = "usage: $0 <sourceFile1> <sourceFile2> ... <destinationDir>\n";
-
- my $targetDir = pop @ARGV;
- my @sourceFiles = @ARGV;
-
- unless($targetDir && @sourceFiles) {
- die $usage;
- }
-
- $result = MacTransferDiff::transferDiff(@sourceFiles, $targetDir);
- }
-
- exit($result);
-
- package MacTransferDiff;
-
- use strict;
- use File::Basename;
- use File::Find;
-
- local @MacTransferDiff::skipPrefixes = ();
- local $MacTransferDiff::sourceFile = "";
- local $MacTransferDiff::baseName = "";
- local $MacTransferDiff::targetDir = "";
-
- local $MacTransferDiff::result = 0;
-
- sub transferDiff {
-
- $MacTransferDiff::sourceFile = "";
- $MacTransferDiff::baseName = "";
- @MacTransferDiff::skipPrefixes = ();
- $MacTransferDiff::targetDir = pop @_;
- my @sourceFiles = @_;
-
- if(!-d $MacTransferDiff::targetDir) {
- print STDERR "$MacTransferDiff::targetDir is not a directory\n" unless -d ;
- return 1;
- }
-
- # append / to targetDir if necessary
- if($MacTransferDiff::targetDir !~ /\/$/) { $MacTransferDiff::targetDir .= "/"; }
-
- foreach $MacTransferDiff::sourceFile (@sourceFiles) {
- if(-d $MacTransferDiff::sourceFile) {
- my $lastDir = $MacTransferDiff::sourceFile;
- $lastDir =~ s/\/$//;
- $MacTransferDiff::baseName = basename($lastDir);
- if($MacTransferDiff::sourceFile !~ /\/$/) { $MacTransferDiff::sourceFile .= "/"; }
- find({ wanted => \&findResult, no_chdir => 1 }, $MacTransferDiff::sourceFile);
- } else {
- macDiff($MacTransferDiff::sourceFile, basename($MacTransferDiff::sourceFile));
- }
- }
-
- return $MacTransferDiff::result;
-
- }
-
- sub findResult {
-
- my $subpath = $File::Find::name;
-
- if(-l $subpath) {
- # if its a link, it may also show up as a directory,
- # a file, or NOT a file if a dangling symlink. In any of
- # these cases, we want to call macDiff on it.
- } else {
- if(-d $subpath . "/Contents/ContainedPackage") {
- # print "Skipping all paths under $subpath\n";
- push(@MacTransferDiff::skipPrefixes, $subpath);
- }
-
- if(-d $subpath && ($subpath !~ /\/$/)) { $subpath .= "/"; }
-
- unless(-r $subpath) {
- print STDERR "$0 skipping $subpath because we can't read the source path\n";
- return;
- }
- }
-
- foreach my $skip (@MacTransferDiff::skipPrefixes) {
- if($subpath =~ /^\Q$skip/) {
- # print "skipping subfile $subpath\n";
- return;
- }
- }
-
- if($subpath eq (substr($MacTransferDiff::sourceFile, 0, length($MacTransferDiff::sourceFile)-1))) {
- # weird case... we find using the full path with / at the end
- # find returns the topmost node without the / for invalid symlink
- # we can't call macDiff with the / at the end, because:
- # invalid_symlink is ok
- # invalid_symlink/ is not
- macDiff($subpath, $MacTransferDiff::baseName);
- } else {
- # gotta use \Q to disable interpretaion of {} () etc
- $subpath =~ s/^\Q$MacTransferDiff::sourceFile//;
- macDiff($MacTransferDiff::sourceFile . $subpath, $MacTransferDiff::baseName . "/" . $subpath);
- }
- }
-
- sub macDiff {
- # sourcefile is full path of source
- # subfile is the node that should exist under targetdir
- my ($sourceFile, $subFile) = @_;
- my $targetFile = $MacTransferDiff::targetDir . $subFile;
- # print STDERR "SOURCE $sourceFile TARGET $targetFile\n";
- if(MacFileDiff::compareNodes($sourceFile, $targetFile, 1)) { $MacTransferDiff::result = 1; }
- }
-
- package MacFileDiff;
-
- use strict;
-
- local $MacFileDiff::headerPrinted = 0;
- local $MacFileDiff::file1 = "";
- local $MacFileDiff::file2 = "";
- local $MacFileDiff::rezHack = "/..namedfork/rsrc";
- local $MacFileDiff::indent = " ";
-
- sub compareNodes {
-
- $MacFileDiff::headerPrinted = 0;
-
- my $result;
- $MacFileDiff::file1 = shift @_;
- $MacFileDiff::file2 = shift @_;
- my $skipPartialFiles = shift @_;
-
- if(-l $MacFileDiff::file3 || -l $MacFileDiff::file2) {
- my $result = compareSymlinks($MacFileDiff::file1, $MacFileDiff::file2);
- return($result);
- }
-
- if(!-e $MacFileDiff::file1) {
- print STDERR "$MacFileDiff::file1 does not exist\n";
- return 1;
- }
-
- if(!-e $MacFileDiff::file2) {
- print STDERR "$MacFileDiff::file2 does not exist\n";
- return 1;
- }
-
- if (-d $MacFileDiff::file1 || -d $MacFileDiff::file2) {
- my $result = compareDirectories($MacFileDiff::file1, $MacFileDiff::file2);
- return($result);
- }
-
- ########### Metadata
-
-
- my $metadata1 = readMetadata($MacFileDiff::file1);
- my $metadata2 = readMetadata($MacFileDiff::file2);
-
- if($skipPartialFiles) {
- foreach($metadata1, $metadata2) {
- if(/type: "iGpf"/ && /creator: "iGet"/) {
- print STDERR "$0: Skipping partial file comparison\n";
- return 0;
- }
- }
- }
-
- if($metadata1 ne $metadata2) {
-
- my $m1 = "/tmp/macdiff.1." . $$;
- my $m2 = "/tmp/macdiff.2." . $$;
-
- writeMetadata($metadata1, $m1);
- writeMetadata($metadata2, $m2);
-
- my $metadataDifferences = `diff --suppress-common-lines -y -W 70 $m1 $m2`;
- unlink $m1, $m2;
-
- if($metadataDifferences) {
- printHeader();
- print STDERR "Metadata differences:\n";
- my @lines = split("\n", $metadataDifferences);
- print STDERR $MacFileDiff::indent, join("\n" . $MacFileDiff::indent, @lines), "\n";
- $result = 1;
- }
- }
-
- ########### Data fork
-
- compareFiles($MacFileDiff::file1, $MacFileDiff::file2, "Data fork", 1) || { $result = 1 };
-
- ########### Resource fork
-
- compareFiles($MacFileDiff::file1 . $MacFileDiff::rezHack,
- $MacFileDiff::file2 . $MacFileDiff::rezHack, "Resource fork") || { $result = 1 };
-
- $MacFileDiff::headerPrinted && printBar();
-
- return $result;
- }
-
- sub readMetadata {
-
- my ($inputFile, $tmpFile) = @_;
-
- my $metadataCommand = "GetFileInfo " . quotemeta($inputFile) . " | tail +2";
- my $metadata = `$metadataCommand`;
- $? && return("GetFileInfo returned error " . $inputFile);
-
- return $metadata;
- }
-
- sub writeMetadata {
- my ($data, $file) = @_;
- open TMP, ">$file";
- print TMP $data;
- close TMP;
- }
-
- sub compareFiles {
- my ($localFile1, $localFile2, $title, $compareMode) = @_;
-
- my @differences;
- my ($f1mode, $f1length) = (stat($localFile1))[2,7];
- my ($f2mode, $f2length) = (stat($localFile2))[2,7];
-
- if ($f1length != $f2length) {
- push @differences, "lengths ($f1length, $f2length)";
- }
-
- system("cmp", "-s", $localFile1, $localFile2);
- my $cmpResult = ($? >> 8);
-
- if($cmpResult) {
- push @differences, "contents";
- }
-
- if($compareMode && ($f1mode != $f2mode)) {
- push @differences, "perms ($f1mode, $f2mode)";
- }
-
- if(@differences) {
- printHeader();
- print STDERR $title, " diffs: ", join(", ", @differences), "\n";
- return 0;
- }
-
- return 1;
- }
-
- sub printHeader {
- $MacFileDiff::headerPrinted && return;
-
- printBar();
- print STDERR "macdiff found differences between the following items:\n";
- print STDERR $MacFileDiff::indent, $MacFileDiff::file1, "\n";
- print STDERR $MacFileDiff::indent, $MacFileDiff::file2, "\n";
- $MacFileDiff::headerPrinted = 1;
- }
-
- sub printBar {
- print STDERR "***************************************************************************\n";
- }
-
- sub compareSymlinks {
- my ($file1, $file2) = @_;
-
- if((-l $file1) != (-l $file2)) {
- printHeader();
- print STDERR "one is a symlink and the other isn't\n";
- printBar();
- return 1;
- }
-
- my $dest1 = readlink($file1);
- my $dest2 = readlink($file2);
- if($dest1 ne $dest2) {
- printHeader();
- print ETDERR "symlink targets don't match:\n";
- print $MacFileDiff::indent, "$file1 -> $dest1\n", $MacFileDiff::indent, "$file2 -> $dest2\n";
- printBar();
- return 1;
- }
- return 0;
- }
-
- sub compareDirectories {
- my ($dir1, $dir2) = @_;
-
- if((-d $dir1) != (-d $dir2)) {
- printHeader();
- print STDERR "one is a folder and the other isn't\n";
- printBar();
- return 1;
- }
-
- my ($f1mode, $f1date) = (stat($dir1))[2,9];
- my ($f2mode, $f2date) = (stat($dir2))[2,9];
-
- my @differences;
- if($f1mode != $f2mode) {
- push @differences, "perms ($f1mode, $f2mode)";
- }
-
- if($f1date != $f2date) {
- push @differences, "mod date ($f1date, $f2date)";
- }
-
- if(@differences) {
- printHeader();
- print STDERR "differences: ", join(", ", @differences), "\n";
- return 1;
- }
-
- return 0;
- }
-